<?php
//2010-01-12 11:09AM EST
class jsonrpc2{
    public $errors 	= array(
    	-32600	=>'Invalid Request',
    	-32601	=>'Method not found',	
    	-32602	=>'Invalid params',	
    	-32603	=>'Internal error',
		-32700	=>'Parse error',
    );
	private $methods 	= array();
	
	public function __construct($method_map){
    	if(!is_array($method_map)) throw new Exception('Requires a method map that is an array.');
        $this->methods = $method_map;
    }

    public function dispatch_batch($request){
    	if(is_array($request)){
    		shuffle($request);
    		$return = array();
    		foreach($request as $i=>$request_item){
    			$a = $this->dispatch($request_item);
    			if(is_object($a)) $return[] = $a;
    		}
    		return $return;
    	}
    	return $this->dispatch($request);
    }
    
    public function dispatch($request){
    	if(!isset($request->jsonrpc)) $request->jsonrpc = '1.0';
        if($request->jsonrpc == '2.0' && !isset($request->params)) $request->params = array();
        $response = $this->invoke($request);
        if(!isset($request->id) || is_null($request->id)) return;
		$response->id = $request->id;
        if($request->jsonrpc == '2.0') $response->jsonrpc = '2.0';
        else{
        	if(!isset($response->result)) $response->result = null;
        	if(!isset($response->error)) $response->error = null;
        }
        return $response;
    }
    
    private function invoke($request){
    	$error = -32601;
        try{
            if(!isset($this->methods[$request->method])) throw new Exception(-32601);
            $m = $this->methods[$request->method];
            if(!is_array($m)) $m = array($m);
            $r = isset($m[1]) ? new ReflectionMethod($m[1], $m[0]) : new ReflectionFunction($m[0]);
            $p = $this->checkParams($r->getParameters(), $request->params);
            if(!isset($m[1]))       return (object) array('result' =>$r->invokeArgs($p));
            if(is_object($m[1]))    return (object) array('result' =>$r->invokeArgs($m[1], $p));
            if(is_string($m[1]))    return (object) array('result' =>$r->invokeArgs(new $m[1], $p));
        }
        catch(Exception $e){
        	$error = $e->getMessage();
        }
        return (object) array('error'=> (object) array('code'=> $error, 'message'=>$this->errors[$error]));
    }
	
    private function checkParams($real, $sent){
        $new = array();
        $is_obj = is_object($sent);
        $is_assoc = array_keys($sent) != range(0, count($sent) - 1);
        foreach($real as $i=>$param)
        {
        	$name = $param->getName();
            if($is_obj &&  isset($sent->{$name})) $new[$i] = $sent->{$name};
            elseif($is_assoc && $sent[$name]) $new[$i] = $sent[$name];
            elseif( isset($sent[$i])) $new[$i] = $sent[$i];
            elseif(!$param->isOptional()) throw new Exception(-32602);
        }
        return $new;
    }
}
?>
